﻿using System;
using System.Collections.Generic;
using System.Text;
using DotNetNinjaLibrary.Basics;
using System.ComponentModel;
using DotNetNinjaLibrary.Actions;
using DotNetNinjaLibrary.Pathfinding;
using System.Linq;

namespace DotNetNinjaLibrary
{
	public abstract class Unit : INotifyPropertyChanged
	{
		private Point location;
		private Size size;

		private List<IUnitAction> actions = new List<IUnitAction>();

		public Unit(Size size)
		{
			this.size = size;
		}

		public bool PlaceOnMap(MapModel map, Point location)
		{
			if (map.IsMovePossible(location, this.Size))
			{
				this.Map = map;
				this.location = location;
				this.Map.AddUnit(this);
				return true;
			}
			return false;
		}

		public MapModel Map { get; private set; }

		public long Speed { get; set; }

		public abstract string Model { get; }

		public Point Location
		{
			get
			{
				return this.location;
			}
		}

		public Size Size
		{
			get
			{
				return this.size;
			}
		}

		private void OnPropertyChanged(string propertyName)
		{
			this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
		}

		protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
		{
			if (this.PropertyChanged != null)
			{
				this.PropertyChanged(this, e);
			}
		}

		public event PropertyChangedEventHandler PropertyChanged;

		public Queue<IUnitAction> upcommingActions = new Queue<IUnitAction>();
		public IUnitAction currentAction;

		private long lastStepStarted;

		public void GoTo(Basics.Point destination)
		{
			if (this.Map.IsMovePossible(destination, this.size))
			{
				if (this.upcommingActions.Count > 0)
				{
					this.upcommingActions.Clear();
				}

				foreach (var p in PathFinding.GetPath(this.Map, this.size, this.location, destination))
				{
					this.upcommingActions.Enqueue(new MoveAction(this, p));
				}

				if (currentAction != null)
				{
					if (this.upcommingActions.Count > 0)
					{
						currentAction = this.upcommingActions.Dequeue();
					}
					else
					{
						currentAction = null;
						lastStepStarted = 0;
					}
				}
			}
		}

		public void Update(long gameTime)
		{
			this.OnUpdated(gameTime);
		}

		protected virtual void OnUpdated(long gameTime)
		{
			if (currentAction == null && this.upcommingActions.Count > 0)
			{
				lastStepStarted = gameTime;
				currentAction = this.upcommingActions.Dequeue();
			}
			else if (currentAction != null)
			{
				var timeToSpend = gameTime - lastStepStarted;
				var timeForAction = currentAction.TimeNeeded;

				while (currentAction != null && timeToSpend >= timeForAction && currentAction.CanExecute())
				{
					lastStepStarted += timeForAction;
					timeToSpend -= timeForAction;
					currentAction.Execute();
					currentAction = this.upcommingActions.Count > 0 ? this.upcommingActions.Dequeue() : null;
				}

				if (currentAction != null && timeToSpend >= timeForAction)
				{
					this.upcommingActions.Clear();
				}
			}
			else
			{
				lastStepStarted = 0;
			}
		}

		public bool MoveTo(Point point)
		{
			if (this.location != point && this.Map.IsMovePossible(point, this.size))
			{
				this.location = point;
				this.Map.MoveUnit(this, point);
				this.OnPropertyChanged("Location");
				return true;
			}
			return false;
		}
	}
}
